package btanalyzer.db.analyzer;

import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import btanalyzer.core.DiggResult;
import btanalyzer.core.DiggResultDiggedSite;
import btanalyzer.core.DiggResultDiggedSite.DRCategory;
import btanalyzer.core.DiggResultTorrent;
import btanalyzer.core.Digger;
import btanalyzer.db.dao.AnnounceUrlDao;
import btanalyzer.db.dao.CategoryDao;
import btanalyzer.db.dao.DiggTaskDao;
import btanalyzer.db.dao.DiggedSiteDao;
import btanalyzer.db.model.Category;
import btanalyzer.db.model.DiggTask;
import btanalyzer.db.model.DiggedSite;
import btanalyzer.db.model.Torrent;

@Component
public class AnalyzerImpl implements Analyzer {
	
	private static final Logger log = LoggerFactory.getLogger(AnalyzerImpl.class);
	
	/**
	 * @see java.lang.Runnable#run()
	 */
	@Override
	@Transactional
	public void run() {
		
		String dirToAnalyzePath = "download";
		
		Category videoCat = new Category();
		Category programsCat = new Category();
		Category gamesCat = new Category();
		videoCat.setName("video");
		programsCat.setName("programs");
		gamesCat.setName("games");

		videoCat = getCategoryDao().save(videoCat);
		gamesCat = getCategoryDao().save(gamesCat);
		programsCat = getCategoryDao().save(programsCat);
		
		Map<DRCategory, Category> categoryMap = new HashMap<DRCategory, Category>();
		categoryMap.put(DRCategory.VIDEO, videoCat);
		categoryMap.put(DRCategory.PROGRAMS, programsCat);
		categoryMap.put(DRCategory.GAMES, gamesCat);
		
		
		// for each direcotry
		for (String downloadDir : new File(dirToAnalyzePath).list()) {
			DiggTask diggTask = new DiggTask();
			Set<DiggedSite> diggedSites = new HashSet<DiggedSite>();
			
			// config
			String diggTaskInfoFilePath = dirToAnalyzePath + "/" + downloadDir + "/info/aboutTask.txt";
			String htmlDirPath = dirToAnalyzePath + "/" + downloadDir + "/html/";
			String metadataDirPath = dirToAnalyzePath + "/" + downloadDir + "/metadata/";
			
			
			// diggTask info
			collectDigTaskInfo(diggTask, diggTaskInfoFilePath);
			
			List<DiggResult> diggResults = digger.digg(htmlDirPath);
			for (DiggResult diggResult : diggResults) {
				DiggedSite diggedSite = new DiggedSite();
				Set<Torrent> torrents = new HashSet<Torrent>();
				DiggResultDiggedSite drDiggedSite = diggResult.getDiggResultDiggedSite();
				List<DiggResultTorrent> drTorrents = diggResult.getDiggResultTorrents();
				
				collectDiggedSiteInfo(diggedSite, drDiggedSite, categoryMap);
				collectTorrentsInfo(torrents, drTorrents, diggedSite, metadataDirPath);

				// db
				diggedSite.setTorrents(torrents);
				diggedSite.setDiggTask(diggTask);
				diggedSites.add(diggedSite);
			}
			
			System.out.println(diggTask);
			diggTask.setDiggedSites(diggedSites);
			diggTask = getDiggTaskDao().save(diggTask);
			getDiggTaskDao().flush();
		}
		
		
	}
	
	private void collectTorrentsInfo(Set<Torrent> torrents,
			List<DiggResultTorrent> drTorrents, DiggedSite diggedSite, 
			String metadataDirPath) {
		for (DiggResultTorrent drTorrent : drTorrents) {
			Torrent t = new Torrent();
			t.setAdditionalInfo(t.getAdditionalInfo());
			t.setDiggedSite(diggedSite);
			t.setMetadataFilePath(metadataDirPath + drTorrent.getFileName());
			t.setMetadataUrl(drTorrent.getUrl());
			t.setPeers(drTorrent.getPeers());
			t.setSeeds(drTorrent.getSeeds());
			torrents.add(t);
		}
		
	}

	@Transactional
	public void collectDiggedSiteInfo(DiggedSite diggedSite,
			DiggResultDiggedSite drDiggedSite, Map<DRCategory, Category> categoryMap) {
		System.out.println("drDiggedSite.getCategory().name()=" + drDiggedSite.getCategory().name());
		Category cat = categoryMap.get(drDiggedSite.getCategory());
		cat.getDiggedSites().add(diggedSite);
		diggedSite.setDownloadedDate(new Date(new File(
				drDiggedSite.getHtmlFilePath()).lastModified()));
		diggedSite.setCategory(cat);
		diggedSite.setFilePath(drDiggedSite.getHtmlFilePath());
		diggedSite.setUrl(drDiggedSite.getUrl());
	}

	@Transactional
	public void collectDigTaskInfo(DiggTask diggTask, String diggTaskInfoFilePath) {
		Properties diggTaskProps = new Properties();
		try {
			FileInputStream fis = new FileInputStream(diggTaskInfoFilePath);
			diggTaskProps.load(fis);
			fis.close();
		} catch (Exception e) {
			String errMsg = "Error while reading file=" + diggTaskInfoFilePath;
			log.error(errMsg, e);
			throw new RuntimeException(errMsg, e);
		}
		Date startDate = new Date(Long.valueOf(diggTaskProps.getProperty("startTimestamp")));
		String stopTimestamp = diggTaskProps.getProperty("stopTimestamp");
		Date stopDate = stopTimestamp == null ? null : new Date(Long.valueOf(stopTimestamp));
		diggTask.setStartDate(startDate);
		diggTask.setStopDate(stopDate);
	}
	
	/* ********************************************************************** */

	@Autowired
	private Digger digger;
	public void setDigger(Digger digger) {
		this.digger = digger;
	}
	public Digger getDigger() {
		return digger;
	}
	
	@Autowired
	DiggTaskDao diggTaskDao;
	public void setDiggTaskDao(DiggTaskDao diggTaskDao) {
		this.diggTaskDao = diggTaskDao;
	}
	public DiggTaskDao getDiggTaskDao() {
		return diggTaskDao;
	}
	
	@Autowired
	CategoryDao categoryDao;
	public void setCategoryDao(CategoryDao categoryDao) {
		this.categoryDao = categoryDao;
	}
	public CategoryDao getCategoryDao() {
		return categoryDao;
	}
	
	@Autowired
	DiggedSiteDao diggedSiteDao;
	public void setDiggedSiteDao(DiggedSiteDao diggedSiteDao) {
		this.diggedSiteDao = diggedSiteDao;
	}
	public DiggedSiteDao getDiggedSiteDao() {
		return diggedSiteDao;
	}
	
	@Autowired
	AnnounceUrlDao announceUrlDao;
	public void setAnnounceUrlDao(AnnounceUrlDao announceUrlDao) {
		this.announceUrlDao = announceUrlDao;
	}
	public AnnounceUrlDao getAnnounceUrlDao() {
		return announceUrlDao;
	}
}
